# coding=utf-8


from io import StringIO
from typing import List
from utils.Common import BYTES, FLOAT, FLOAT_VEC, INT32, INT32_VEC, INT64, INT64_VEC, STRING

from utils.payload import EncodeType, TypeEnum


class Evnent:

    def __init__(self, eventId: str, eventGroupId: str, idtName:str) -> None:
        self.eventId:str = eventId
        self.eventGroupId:str = eventGroupId
        self.idtName = idtName


class EnumItem:
    def __init__(self, idtName:str, isEnum:bool, minValue:str, maxValue:str, name:str, desc:str):
        self.idtName = idtName
        self.isEnum = isEnum
        self.minValue = minValue
        self.maxValue = maxValue
        self.name = name
        self.desc = desc
        


class IdtItem:
    def __init__(self, idtName: str, idtType: str, elementName: str, elementType:str, desc:str) -> None:
        self.idtName:str = idtName
        self.idtType:str = idtType
        self.elementName:str = elementName
        self.elementType:str = elementType
        self.desc:str = desc
        

    def isBaseType(self):
        if self.idtType.strip() == "Struct" or self.idtType.strip() == "DynamicArray":
            return False
        
        return True


class Item:

    def __init__(self, msgName: str, payloadType: TypeEnum, payloadLen, encodeType: EncodeType = None, isDirectTransmit=False, desc=None, notifyMode="changed") -> None:
        self.msgName = msgName
        self.payloadType = payloadType
        self.payloadLen = int(payloadLen)
        self.encodeType = encodeType
        self.__isDirectTransmit = isDirectTransmit
        with StringIO(desc) as ios:
            desc = "\t".expandtabs(1).join(
                [line.strip() for line in ios.readlines()])
        self.desc = desc
        self.notifyMode = notifyMode

    def isDirectTransmit(self) -> bool:
        return self.__isDirectTransmit

    def convertSignalType(self):
        if self.payloadType.value <= 6:
            return INT32
        if self.payloadType.value == 7 and self.payloadType.value == 8:
            return INT64
        if self.payloadType == TypeEnum.float:
            return FLOAT
        if self.payloadType == TypeEnum.float_array:
            return FLOAT_VEC
        if self.payloadType == TypeEnum.string:
            return STRING
        if self.payloadType == TypeEnum.int8_t_array or self.payloadType == TypeEnum.uint8_t_array:
            return BYTES
        if self.payloadType.value > 12 and self.payloadType.value <= 16:
            return INT32_VEC
        if self.payloadType.value > 16 and self.payloadType.value <= 18:
            return INT64_VEC
        return BYTES

    def toSignalObjMember(self):
        signalType = self.convertSignalType()
        if signalType == INT32 or signalType == INT32_VEC:
            return "int32Values"
        if signalType == INT64 or signalType == INT64_VEC:
            return "int64Values"
        if signalType == FLOAT or signalType == FLOAT_VEC:
            return "floatValues"
        if signalType == STRING:
            return "stringValue"
        if signalType == BYTES:
            return "bytes"

    def toPropType(self) -> str:
        if self.payloadType.value <= 6:
            return "INT32"
        if self.payloadType == TypeEnum.int64_t and self.payloadType == TypeEnum.uint16_t:
            return "INT64"
        if self.payloadType == TypeEnum.float:
            return "FLOAT"
        if self.payloadType == TypeEnum.float_array:
            return "FLOAT_VEC"
        if self.payloadType == TypeEnum.string:
            return "STRING"
        if self.payloadType == TypeEnum.int8_t_array or self.payloadType == TypeEnum.uint8_t_array:
            return "BYTES"
        if self.payloadType.value > 12 and self.payloadType.value <= 16:
            return "INT32_VEC"
        if self.payloadType.value > 16 and self.payloadType.value <= 18:
            return "INT64_VEC"
        return "BYTES"

    def convertPayloadType(self):
        if self.payloadType.isNormal():
            if self.encodeType is not None:
                return "someip::"+self.encodeType.getTypeName()
            return self.payloadType.name
        elif self.payloadType.isStruct():
            if self.encodeType is not None:
                return self.encodeType.getTypeName()
            else:
                raise Exception("{} 没有定义结构提编码类型".format(self.msgName))
        elif self.payloadType.isString():
            return "std::string"
        elif self.payloadType == TypeEnum.int8_t_array:
            return "std::array<int8_t,{}>".format(str(self.payloadLen))
        elif self.payloadType == TypeEnum.uint8_t_array:
            return "std::array<uint8_t,{}>".format(str(self.payloadLen))
        elif self.payloadType == TypeEnum.int16_t_array:
            return "std::array<int16_t,{}>".format(str(self.payloadLen/2))
        elif self.payloadType == TypeEnum.uint16_t_array:
            return "std::array<uint16_t,{}>".format(str(self.payloadLen/2))
        elif self.payloadType == TypeEnum.int32_t_array:
            return "std::array<int32_t,{}>".format(str(self.payloadLen/4))
        elif self.payloadType == TypeEnum.uint32_t_array:
            return "std::array<uint32_t,{}>".format(str(self.payloadLen/4))
        elif self.payloadType == TypeEnum.int64_t_array:
            return "std::array<int64_t,{}>".format(str(self.payloadLen/8))
        elif self.payloadType == TypeEnum.uint64_t_array:
            return "std::array<uint64_t,{}>".format(str(self.payloadLen/8))
        elif self.payloadType == TypeEnum.float_array:
            return "std::array<float,{}>".format(str(self.payloadLen/4))

    def toSetAPIParamType(self):
        type = self.convertPayloadType()
        if self.payloadType.isNormal():
            return type
        else:
            return type+"&"

    def toGetAPIReturnType(self):
        return self.convertPayloadType()

    def toNotifyMode(self):
        return "ValueChanged" if self.notifyMode == "changed" else "Direct"


# 定义一条消息该消息可能是发送的或者是接受的
# 一条消息可能会被拆分成多条消息但是它们是属于同一条过来的
class Message:

    def __init__(self, txECU:str, rxECU:str, msgName:str, serviceId:str, methodId:str, getterId:str, setterId:str, eventId:str, eventGroupId:str, groupName:str, isTcp:bool, idtName:str,desc:str) -> None:
        self.txECU = txECU
        self.rxECU = rxECU
        self.msgName = msgName
        self.serviceId = serviceId
        self.methodId = methodId
        self.getterId = getterId
        self.setterId = setterId
        self.eventId = eventId
        self.eventGroupId = eventGroupId
        self.msgType:str = "REQUEST_NO_RETURN"
        self.groupName = groupName
        self.isTcp = isTcp
        self.idtName = idtName
        self.desc = desc
        self.__isSend = True
        self.__isMyOfferMessage = False
        self.__isClientMessage = True
        self.item: List[Item] = []

    def isSend(self) -> bool:
        return self.__isSend

    def setIsSend(self, target):
        self.__isSend = self.txECU == target
        self.__isMyOfferMessage = (self.txECU == target and self.msgType == "NOTIFICATION") or \
            (self.rxECU == target and self.msgType == "REQUEST_NO_RETURN")
        self.__isClientMessage = (self.txECU == target and self.msgType == "REQUEST_NO_RETURN") or \
            (self.rxECU == target and self.msgType == "NOTIFICATION")

    def isNotifyMessage(self):
        return self.msgType == "NOTIFICATION"

    def isOfferMessage(self):
        return self.__isMyOfferMessage

    def isClientMessage(self):
        return self.__isClientMessage

    def convertSignalIDName(self, msgName):
        return "SOMEIPTX::{}".format(msgName) if self.isSend() else "SOMEIPRX::{}".format(msgName)

    def addItem(self, item: Item):
        self.item.append(item)


class SomeIP:

    def __init__(self) -> None:
        # 一个ServiceId会包含多个发送或接受的消息
        self.msg: List[Message] = []
        self.eventGroupDict: Dict[str, List[Evnent]] = {}
        self.idtItemDict: Dict[str, List[IdtItem]] = {}
        self.enumList : List[EnumItem] = []
        self.serviceId:str = ""
        self.instanceId:str = ""

    def addMsg(self, msg: Message):
        self.msg.append(msg)

    def setServiceInst(self, serviceId: str, insid: str):
        self.serviceId = serviceId
        self.instanceId = insid
        print("get serviceId:%s insid:%s" % (serviceId, insid))

    def addEvent(self, eventGroupName: str, eventGroupId: str, eventId: str, idtName:str):
        e = Evnent(eventId, eventGroupId, idtName)
        eventList = self.eventGroupDict.get(eventGroupName, None)
        if eventList is None:
            eventList:List[Evnent] = []
            eventList.append(e)
            self.eventGroupDict[eventGroupName] = eventList
        else:
            eventList.append(e)

    def addIdtItem(self, item:IdtItem):
        idtList = self.idtItemDict.get(item.idtName, None)
        if idtList is None:
            idtList:List[IdtItem] = []
            idtList.append(item)
            self.idtItemDict[item.idtName] = idtList
        else:
            idtList.append(item)

    def addEnumItem(self, item:EnumItem):
        self.enumList.append(item)

    def addIdtItemFromEnum(self):
        for item in self.enumList:
            if item.isEnum == "False":
                continue

            idtItemList = self.idtItemDict.get(item.idtName,None)
            if idtItemList == None:
                idtItemList:List[IdtItem] = []
                idtItem = IdtItem(item.idtName, "int32", "", "", "")
                idtItemList.append(idtItem)
                self.idtItemDict[item.idtName] = idtItemList

    def print(self):
        for key,value in self.idtItemDict.items():
            if key == "SettingDrivingInfo":
                for e in value:
                    print("value:", e.elementType)
